<template>
  <div class="paint-root">
    <div class="paint-toolbar">
      <div class="paint-tools">
        <button :class="{active: tool==='pen'}" @click="tool='pen'" title="画笔">🖊️</button>
        <button :class="{active: tool==='eraser'}" @click="tool='eraser'" title="橡皮擦">🧽</button>
        <button :class="{active: tool==='line'}" @click="tool='line'" title="直线">/</button>
        <button :class="{active: tool==='rect'}" @click="tool='rect'" title="矩形">▭</button>
        <button :class="{active: tool==='circle'}" @click="tool='circle'" title="圆">◯</button>
        <button :class="{active: tool==='arrow'}" @click="tool='arrow'" title="箭头">➔</button>
        <button :class="{active: tool==='star'}" @click="tool='star'" title="星形">★</button>
        <button :class="{active: tool==='text'}" @click="tool='text'" title="文字">A</button>
      </div>
      <div class="paint-colors">
        <span v-for="c in colors" :key="c" :style="{background: c}" :class="['color-dot', {active: color===c}]" @click="color=c"></span>
        <input type="color" v-model="color" title="自定义颜色" />
      </div>
      <div class="paint-sizes">
        <input type="range" min="1" max="30" v-model="size" />
        <span>{{ size }}</span>
      </div>
      <div class="paint-actions">
        <button @click="undo" :disabled="!canUndo" title="撤销">↶</button>
        <button @click="redo" :disabled="!canRedo" title="重做">↷</button>
        <button @click="clearCanvas" title="清空">🗑️</button>
        <button @click="saveImage" title="保存">💾</button>
      </div>
    </div>
    <div class="paint-canvas-wrap">
      <canvas ref="canvas" :width="canvasW" :height="canvasH" class="paint-canvas" @mousedown="onDown" @mousemove="onMove" @mouseup="onUp" @mouseleave="onUp" @dblclick="onDblClick"></canvas>
      <input v-if="showTextInput" ref="textInput" class="paint-text-input" v-model="textValue" :style="textInputStyle" @blur="commitText" @keydown.enter="commitText" />
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, nextTick } from 'vue'

const canvas = ref(null)
const ctx = ref(null)
const canvasW = 1152
const canvasH = 648

const tool = ref('pen')
const color = ref('#000000')
const size = ref(4)
const colors = [
  '#000000','#444','#888','#ccc','#fff',
  '#e74c3c','#e67e22','#f1c40f','#2ecc71','#3498db','#9b59b6',
  '#ff9800','#ffeb3b','#00bcd4','#8bc34a','#cddc39','#e91e63','#673ab7'
]

const drawing = ref(false)
const start = reactive({x:0, y:0})
const last = reactive({x:0, y:0})
const history = ref([])
const redoStack = ref([])

const canUndo = computed(()=>history.value.length>0)
const canRedo = computed(()=>redoStack.value.length>0)

function pushHistory() {
  if (!ctx.value) return
  history.value.push(canvas.value.toDataURL())
  if (history.value.length>50) history.value.shift()
  redoStack.value = []
}

function undo() {
  if (!canUndo.value) return
  redoStack.value.push(canvas.value.toDataURL())
  const data = history.value.pop()
  const img = new window.Image()
  img.onload = ()=>{
    ctx.value.clearRect(0,0,canvasW,canvasH)
    ctx.value.drawImage(img,0,0)
  }
  img.src = data
}

function redo() {
  if (!canRedo.value) return
  history.value.push(canvas.value.toDataURL())
  const data = redoStack.value.pop()
  const img = new window.Image()
  img.onload = ()=>{
    ctx.value.clearRect(0,0,canvasW,canvasH)
    ctx.value.drawImage(img,0,0)
  }
  img.src = data
}

function clearCanvas() {
  pushHistory()
  ctx.value.clearRect(0,0,canvasW,canvasH)
}

function saveImage() {
  const url = canvas.value.toDataURL('image/png')
  const a = document.createElement('a')
  a.href = url
  a.download = 'paint.png'
  a.click()
}

function setStyle() {
  ctx.value.strokeStyle = color.value
  ctx.value.fillStyle = color.value
  ctx.value.lineWidth = size.value
  ctx.value.lineCap = 'round'
  ctx.value.lineJoin = 'round'
}

function onDown(e) {
  drawing.value = true
  start.x = last.x = e.offsetX
  start.y = last.y = e.offsetY
  if (tool.value==='pen'||tool.value==='eraser') {
    pushHistory()
    ctx.value.beginPath()
    ctx.value.moveTo(start.x, start.y)
    setStyle()
    if (tool.value==='eraser') ctx.value.strokeStyle = '#fff'
  }
}

function onMove(e) {
  if (!drawing.value) return
  const x = e.offsetX, y = e.offsetY
  if (tool.value==='pen'||tool.value==='eraser') {
    ctx.value.lineTo(x, y)
    ctx.value.stroke()
    last.x = x; last.y = y
  }
}

function onUp(e) {
  if (!drawing.value) return
  drawing.value = false
  const x = e?.offsetX ?? last.x, y = e?.offsetY ?? last.y
  if (tool.value==='pen'||tool.value==='eraser') {
    ctx.value.closePath()
  } else if (["line","rect","circle","arrow","star"].includes(tool.value)) {
    pushHistory()
    drawShape(tool.value, start.x, start.y, x, y)
  }
}

function drawShape(type, x1, y1, x2, y2) {
  setStyle()
  ctx.value.save()
  ctx.value.beginPath()
  if (type==='line') {
    ctx.value.moveTo(x1, y1)
    ctx.value.lineTo(x2, y2)
    ctx.value.stroke()
  } else if (type==='rect') {
    ctx.value.strokeRect(x1, y1, x2-x1, y2-y1)
  } else if (type==='circle') {
    const r = Math.hypot(x2-x1, y2-y1)
    ctx.value.arc(x1, y1, r, 0, Math.PI*2)
    ctx.value.stroke()
  } else if (type==='arrow') {
    drawArrow(x1, y1, x2, y2)
  } else if (type==='star') {
    drawStar(x1, y1, x2, y2)
  }
  ctx.value.restore()
}

function drawArrow(x1, y1, x2, y2) {
  const headlen = 15 + size.value
  const dx = x2 - x1, dy = y2 - y1
  const angle = Math.atan2(dy, dx)
  ctx.value.moveTo(x1, y1)
  ctx.value.lineTo(x2, y2)
  ctx.value.lineTo(x2-headlen*Math.cos(angle-Math.PI/6), y2-headlen*Math.sin(angle-Math.PI/6))
  ctx.value.moveTo(x2, y2)
  ctx.value.lineTo(x2-headlen*Math.cos(angle+Math.PI/6), y2-headlen*Math.sin(angle+Math.PI/6))
  ctx.value.stroke()
}

function drawStar(x1, y1, x2, y2) {
  const spikes = 5
  const outerRadius = Math.hypot(x2-x1, y2-y1)
  const innerRadius = outerRadius/2.5
  let rot = Math.PI/2*3
  let cx = x1, cy = y1
  ctx.value.moveTo(cx, cy-outerRadius)
  for(let i=0;i<spikes;i++){
    ctx.value.lineTo(cx+Math.cos(rot)*outerRadius, cy+Math.sin(rot)*outerRadius)
    rot += Math.PI/spikes
    ctx.value.lineTo(cx+Math.cos(rot)*innerRadius, cy+Math.sin(rot)*innerRadius)
    rot += Math.PI/spikes
  }
  ctx.value.lineTo(cx, cy-outerRadius)
  ctx.value.closePath()
  ctx.value.stroke()
}

// 文字输入
const showTextInput = ref(false)
const textValue = ref('')
const textInputStyle = ref({})
const textInput = ref(null)
function onDblClick(e) {
  if (tool.value!=='text') return
  showTextInput.value = true
  textValue.value = ''
  textInputStyle.value = {
    position:'absolute',
    left: e.offsetX+8+'px',
    top: e.offsetY+8+'px',
    font: '20px sans-serif',
    color: color.value,
    border: '1px solid #888',
    background: '#fff',
    zIndex: 10
  }
  nextTick(()=>textInput.value && textInput.value.focus())
  textInput.value._paintPos = {x: e.offsetX, y: e.offsetY}
}
function commitText() {
  if (!showTextInput.value) return
  setStyle()
  ctx.value.save()
  ctx.value.font = '20px sans-serif'
  ctx.value.fillStyle = color.value
  const {x, y} = textInput.value._paintPos
  ctx.value.fillText(textValue.value, x, y+20)
  ctx.value.restore()
  showTextInput.value = false
  textValue.value = ''
  pushHistory()
}

onMounted(()=>{
  ctx.value = canvas.value.getContext('2d')
  ctx.value.clearRect(0,0,canvasW,canvasH)
})
</script>

<style scoped lang="scss">
$primary: #0078d7;
$toolbar-bg: #f3f6fb;
$border: #e0e0e0;
$canvas-bg: #fff;
$shadow: 0 2px 8px #0001;

.paint-root {
  width: 100%;
  height: 100%;
  background: #f8fafc;
  display: flex;
  flex-direction: column;

  .paint-toolbar {
    display: flex;
    align-items: center;
    background: $toolbar-bg;
    border-bottom: 1px solid $border;
    padding: 6px 10px;
    gap: 18px;
    user-select: none;

    .paint-tools {
      button {
        margin-right: 4px;
        font-size: 18px;
        background: none;
        border: 1px solid #ddd;
        border-radius: 4px;
        padding: 2px 7px;
        cursor: pointer;
        transition: background .2s;

        &.active {
          background: #e3eefd;
          border-color: $primary;
        }
      }
    }
    .paint-colors {
      display: flex;
      align-items: center;
      gap: 2px;
      .color-dot {
        width: 18px;
        height: 18px;
        border-radius: 50%;
        border: 1px solid #bbb;
        margin-right: 2px;
        cursor: pointer;
        box-sizing: border-box;
        &.active {
          border: 2px solid $primary;
        }
      }
    }
    .paint-sizes {
      display: flex;
      align-items: center;
      gap: 4px;
    }
    .paint-actions {
      button {
        margin-right: 4px;
        font-size: 16px;
        background: none;
        border: 1px solid #ddd;
        border-radius: 4px;
        padding: 2px 7px;
        cursor: pointer;
        transition: background .2s;
        &:disabled {
          opacity: 0.4;
          cursor: not-allowed;
        }
      }
    }
  }
  .paint-canvas-wrap {
    flex: 1;
    position: relative;
    background: $canvas-bg;
    overflow: auto;
    display: flex;
    justify-content: center;
    align-items: center;
    .paint-canvas {
      background: $canvas-bg;
      border: 1px solid $border;
      border-radius: 4px;
      box-shadow: $shadow;
      display: block;
      margin: 0 auto;
    }
    .paint-text-input {
      position: absolute;
      min-width: 80px;
      min-height: 28px;
      font-size: 20px;
      border: 1px solid #888;
      background: #fff;
      z-index: 10;
      outline: none;
      padding: 2px 6px;
    }
  }
}
</style>
